package pap;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.Semaphore;

import pap.bodies.SimpleBody;
import pap.gui.BodyShaper;
import pap.gui.IShape;


public class BufferManager {

	static BufferManager instance;
	TimesRegister timesRegister;
	LinkedBlockingDeque<List<IShape>> buffer;
	List<IShape> currentBufferList;

	Semaphore availablePlaces, availableSteps;
	BodyShaper shaper;

	private BufferManager(int bufferSize, TimesRegister timesRegister) {

		this.timesRegister = timesRegister;
		shaper = new BodyShaper();

		buffer = new LinkedBlockingDeque<List<IShape>>();
		currentBufferList = new ArrayList<IShape>();
		buffer.add(currentBufferList);

		// The first list is available yet
		availablePlaces = new Semaphore(bufferSize - 1);
		// The buffer is empty
		availableSteps = new Semaphore(0);
	}

	/**
	 * Buffer initialization
	 * 
	 * @param bufferSize
	 * @param timesRegister
	 */
	public static void init(int bufferSize, TimesRegister timesRegister) {
		instance = new BufferManager(bufferSize, timesRegister);
	}

	public void startNewStep() throws InterruptedException {

		// Release a permission to the consumer
		availableSteps.release();

		// Wait until the buffer is not full
		availablePlaces.acquire();

		// Add a new list to the buffer
		currentBufferList = new ArrayList<IShape>();
		buffer.add(currentBufferList);

	}

	/**
	 * Add a new body to the buffer. Only SimpleBody are accepted.
	 * 
	 * @param body
	 */
	public void addBody(SimpleBody body) {
		currentBufferList.add(shaper.getBodyShape(body));
	}

	/**
	 * Retrieves and returns one step from the head of the buffer.
	 * 
	 * @return Step
	 * @throws InterruptedException
	 */
	public List<IShape> getNextList() throws InterruptedException {

		timesRegister.recordStep();

		// Release a permission to the producer
		availablePlaces.release();

		// Wait until the buffer is not empty
		availableSteps.acquire();

		return buffer.take();
	}

	/**
	 * Returns the instance of the buffer
	 * 
	 * @return instance
	 */
	public static BufferManager getInstance() {
		return instance;
	}

}